/*****************************************************************************
*    Copyright (C)2003 Ali Corporation. All Rights Reserved.
*
*    File:    nim_stv0299.h
*
*    Description:    This file contains stv0299 basic function in LLD.
*    History:
*           Date            Athor        Version          Reason
*	    ============	=============	=========	=================
*	1.	Dec.02.2003     David Wang	     Ver 0.1    Create file.
*****************************************************************************/

#include <sys_config.h>
#include <retcode.h>
#include <types.h>
#include <api/libc/alloc.h>
#include <api/libc/printf.h>
#include <api/libc/string.h>
#include <osal/osal_int.h>
#include <osal/osal_task.h>
#include <osal/osal_timer.h>
#include <osal/osal.h>
#include <hal/hal_gpio.h>
#include <bus/i2c/i2c.h>
#include <hld/hld_dev.h>
#include <hld/nim/nim_dev.h>
#include <hld/nim/nim.h>

#include "nim_stv0299.h"
#include "nim_tsa5059.h"


struct nim_stv0299_private
{
	struct   QPSK_TUNER_CONFIG_DATA Tuner_Config_Data;
	INT32 	(*nim_Tuner_Init) (UINT32* tuner_id, struct QPSK_TUNER_CONFIG_EXT * ptrTuner_Config);	/* Tuner Initialization Function */
	INT32 	(*nim_Tuner_Control) (UINT32 tuner_id, UINT32 freq, UINT32 sym);	/* Tuner Parameter Configuration Function */
	INT32 	(*nim_Tuner_Status) (UINT32 tuner_id, UINT8 *lock);		
	UINT32 tuner_id;
	struct nim_device_stats stats;
	UINT8 chip_id;
	UINT32 i2c_type_id;
	UINT32 polar_gpio_num;
	UINT32 sys_crystal;
	UINT32 sys_clock;
	UINT32 cur_freq;
	UINT32 cur_sym;
	UINT8 cur_fec;
};
static OSAL_ID mutex_id = OSAL_INVALID_ID;
#define MUTEX_INIT()							\
	do										\
	{										\
		if(mutex_id==OSAL_INVALID_ID)		\
			mutex_id = osal_mutex_create();	\
		ASSERT(mutex_id!=OSAL_INVALID_ID);	\
	}while(0)
#define MUTEX_LOCK()	osal_mutex_lock(mutex_id, OSAL_WAIT_FOREVER_TIME)
#define MUTEX_UNLOCK()	osal_mutex_unlock(mutex_id)
/*		
static UINT32 stv0299_i2c_type_id;
static UINT32 stv0299_polar_gpio_num;
static UINT32 stv0299_sys_crystal;
static UINT32 stv0299_sys_clock;
static UINT32 cur_freq = 0;
static UINT32 cur_sym = 0;
static UINT8 cur_fec = 0;
*/
/* Name for the tuner, the last character must be Number for index */
static INT8 nim_stv0299_name[HLD_MAX_NAME_SIZE] = "NIM_STV0299_0";

static INT32 nim_stv0299_open(struct nim_device *dev);
static INT32 nim_stv0299_close(struct nim_device *dev);
static INT32 nim_stv0299_ioctl(struct nim_device *dev, INT32 cmd, UINT32 param);
static INT32 nim_stv0299_set_polar(struct nim_device *dev, UINT8 polar);
static INT32 nim_stv0299_set_12v(struct nim_device *dev, UINT8 flag);
static INT32 nim_stv0299_channel_change(struct nim_device *dev, UINT32 freq, UINT32 sym, UINT8 fec);
static INT32 nim_stv0299_channel_search(struct nim_device *dev, UINT32 freq);
static INT32 nim_stv0299_DiSEqC_operate(struct nim_device *dev, UINT32 mode, UINT8* cmd, UINT8 cnt);

static INT32 nim_stv0299_get_lock(struct nim_device *dev, UINT8 *lock);
static INT32 nim_stv0299_get_freq(struct nim_device *dev, UINT32 *freq);
static INT32 nim_stv0299_get_symbol_rate(struct nim_device *dev, UINT32 *sym_rate);
static INT32 nim_stv0299_set_symbol_rate(struct nim_device *dev, UINT32 sym_rate);
static INT32 nim_stv0299_get_code_rate(struct nim_device *dev, UINT8* code_rate);
static INT32 nim_stv0299_get_AGC(struct nim_device *dev, UINT8 *agc);
static INT32 nim_stv0299_get_SNR(struct nim_device *dev, UINT8 *snr);
static INT32 nim_stv0299_get_PER(struct nim_device *dev, UINT32 *RsUbc);

static INT32 nim_stv0299_tuner_lock(struct nim_device *dev, UINT8 *tun_lock);
static INT32 nim_stv0299_get_FEC_lock(struct nim_device *dev, UINT8 *fec_lock);
static INT32 nim_stv0299__check_inversion (struct nim_device *dev);
static INT32 nim_stv0299_read(UINT32 i2c_type_id, UINT8 dev_add,UINT8 reg_add, UINT8 *data);

/* initial SNR lookup table */
LOOKPOINT snr_lookup[] =
{
	{2, 5400},
	{4, 5600},
	{6, 5800},
	{8, 6000},
	{10,6200},
	{12,6400},
	{14,6600},
	{16,6800},
	{18,7000},
	{20,7200},
	{22,7400},
	{24,7600},
	{26,7800},
	{28,8000},
	{30,8200},
	{32,8400},
	{34,8600},
	{36,8800},
	{38,9000},
	{40,9200},
	{42,9400},
	{44,9600},
	{46,9800},
	{48,10000},
	{50,10200},
	{52,10400},
	{54,10600},
	{56,10800},
	{58,11000},
	{60,20000}
};

/* initial AGC1 lookup table */
LOOKPOINT agc1_lookup[] =
{
	{36,37000},
	{38,40000},
	{40,43000},
	{42,46000},
	{44,49000},
	{46,49600},
	{48,50300},
	{50,51000},
	{52,51700},
	{54,52400},
	{56,53100},
	{58,53800},
	{60,54500},
	{62,55200},
	{64,55900},
	{66,56600},
	{68,57300},
	{70,58000},
	{72,58700},
	{74,59400},
	{76,60100},
	{78,60700},
	{80,61400}
};

void dump_st299(struct nim_device *dev)
{
	UINT8 reg_id,val;
	struct nim_stv0299_private *priv_mem = (struct nim_stv0299_private *)dev->priv;
	for(reg_id = 0;reg_id<0x35;reg_id++)
	{
		val = 0;
		nim_stv0299_read(priv_mem->i2c_type_id, dev->base_addr,reg_id,(UINT8 *)&val);
		osal_task_sleep(1);
		PRINTF("%.2x ",val);
		if((reg_id+1)%16 == 0)
			PRINTF("\n");
	}

}

static INT32 nim_stv0299_read(UINT32 i2c_type_id, UINT8 dev_add,UINT8 reg_add, UINT8 *data)
{
	data[0] = reg_add;
	return i2c_write_read(i2c_type_id, dev_add, data, 1, 1);
}

static INT32 nim_stv0299_write(UINT32 i2c_type_id, UINT8 dev_add, UINT8 reg_add, UINT8 data)
{
	INT32 err;
	UINT8 temp[2];

	temp[0] = reg_add;
	temp[1] = data;
	return i2c_write(i2c_type_id, dev_add, &(temp[0]), 2);
}

/*****************************************************************************
* INT32  nim_stv0299_init()
* Description: STV0299 initialization
*
* Arguments:
*  none
*
* Return Value: INT32
*****************************************************************************/
INT32 nim_stv0299_attach(struct QPSK_TUNER_CONFIG_API * tuner_config_api)
{
	struct nim_device *dev;
	struct nim_stv0299_private *priv_mem;
	
	MUTEX_INIT();
	dev = (struct nim_device *)dev_alloc(nim_stv0299_name, HLD_DEV_TYPE_NIM, sizeof(struct nim_device));
	if (dev == NULL)
	{
		PRINTF("Error: Alloc nim device error!\n");
		return ERR_NO_MEM;
	}
	/* Alloc structure space of private */
	priv_mem = (struct nim_stv0299_private  *)MALLOC(sizeof(struct nim_stv0299_private));
	if (priv_mem == NULL)
	{
		dev_free(dev);
		PRINTF("Alloc nim device prive memory error!/n");
		return ERR_NO_MEM;
	}
	MEMSET((UINT8 *)priv_mem, 0, sizeof(struct nim_stv0299_private));
	dev->priv = (void *)priv_mem;

	/* Function point init */
	dev->base_addr = SYS_DEM_BASE_ADDR;
	dev->init = nim_stv0299_attach;
	dev->open = nim_stv0299_open;
	dev->stop = nim_stv0299_close;
	dev->do_ioctl = nim_stv0299_ioctl;
	dev->set_polar = nim_stv0299_set_polar;
	dev->set_12v = nim_stv0299_set_12v;
	dev->channel_change = nim_stv0299_channel_change;
	dev->channel_search = nim_stv0299_channel_search;
	dev->DiSEqC_operate = nim_stv0299_DiSEqC_operate;
	dev->get_lock = nim_stv0299_get_lock;
	dev->get_freq = nim_stv0299_get_freq;
	dev->get_sym = nim_stv0299_get_symbol_rate;
	dev->get_FEC = nim_stv0299_get_code_rate;
	dev->get_AGC = nim_stv0299_get_AGC;
	dev->get_SNR = nim_stv0299_get_SNR;
	dev->get_BER = nim_stv0299_get_PER;
	
	priv_mem->nim_Tuner_Init = tuner_config_api->nim_Tuner_Init;
	priv_mem->nim_Tuner_Control = tuner_config_api->nim_Tuner_Control;
	priv_mem->nim_Tuner_Status = tuner_config_api->nim_Tuner_Status;
	priv_mem->Tuner_Config_Data.Recv_Freq_Low = tuner_config_api->config_data.Recv_Freq_Low;
	priv_mem->Tuner_Config_Data.Recv_Freq_High = tuner_config_api->config_data.Recv_Freq_High;
	priv_mem->Tuner_Config_Data.Ana_Filter_BW = tuner_config_api->config_data.Ana_Filter_BW;
	priv_mem->Tuner_Config_Data.Connection_config = tuner_config_api->config_data.Connection_config;
	priv_mem->Tuner_Config_Data.AGC_Threshold_1 = tuner_config_api->config_data.AGC_Threshold_1;
	priv_mem->Tuner_Config_Data.AGC_Threshold_2 = tuner_config_api->config_data.AGC_Threshold_2;
	priv_mem->Tuner_Config_Data.QPSK_Config = tuner_config_api->config_data.QPSK_Config;
			
	dev->base_addr = tuner_config_api->ext_dm_config.i2c_base_addr;
	priv_mem->sys_crystal = tuner_config_api->ext_dm_config.dm_crystal;
	priv_mem->i2c_type_id = tuner_config_api->ext_dm_config.i2c_type_id;
	priv_mem->polar_gpio_num = tuner_config_api->ext_dm_config.polar_gpio_num;
	priv_mem->sys_clock = tuner_config_api->ext_dm_config.dm_clock;
	/* Add this device to queue */
	if (dev_register(dev) != SUCCESS)
	{
		PRINTF("Error: Register nim device error!\n");
		FREE(priv_mem);
		dev_free(dev);
		return ERR_NO_DEV;
	}
	
	if (priv_mem->nim_Tuner_Init)
	{
		NIM_PRINTF(" %s: Initial the Tuner \n", __FUNCTION__);

		if (priv_mem->nim_Tuner_Init (&priv_mem->tuner_id, &(tuner_config_api->tuner_config)) != SUCCESS)
		{
			NIM_PRINTF("Error: Init Tuner Failure!\n");
			return ERR_NO_DEV;
		}
	}

	return SUCCESS;
}

/*****************************************************************************
* INT32 nim_stv0299_hw_init(struct nim_device *dev)
* Description: STV0299 hardware initialization
*
* Arguments:
*  Parameter1: struct nim_device *dev
*
* Return Value: void
*****************************************************************************/
static INT32 nim_stv0299_hw_init(struct nim_device *dev)
{
	struct nim_stv0299_private *priv_mem = (struct nim_stv0299_private *)dev->priv;
	PRINTF("nim_stv0299_hw_init:$#@@@@@@@@@@@@@@@@@@@@@@@@#$#\n");
	/* clock registers */
    nim_stv0299_write(priv_mem->i2c_type_id, dev->base_addr,REG_RCR,          0X15); /* K = 0, DIRCLK = 0, M = 0x15 */
    nim_stv0299_write(priv_mem->i2c_type_id, dev->base_addr,REG_MCR,          0X30); /* STDBY = 0, VCO = 0 (ON), SERCLK = 0, P = 0 */
    								  /* f_VCO = 4MHz * 4 * (M+1) / (K+1) = 352 MHz */
    nim_stv0299_write(priv_mem->i2c_type_id, dev->base_addr,REG_ACR,          0X2A);
    nim_stv0299_write(priv_mem->i2c_type_id, dev->base_addr,REG_F22FR,        0X7D); /* F22FR = 0x7d, F22 = f_VCO / 128 / 0x7d = 22 kHzs */

 	/* I2C bus repeater */
    nim_stv0299_write(priv_mem->i2c_type_id, dev->base_addr,REG_I2CRPT,       0X05); /* I2CT = 0, SCLT = 1, SDAT = 1 */

 	/* general purpose DAC registers */
    nim_stv0299_write(priv_mem->i2c_type_id, dev->base_addr,REG_DACR1,        0XA2); /* DAC not used, set to high impendance mode */
    nim_stv0299_write(priv_mem->i2c_type_id, dev->base_addr,REG_DACR2,        0X00); /* DAC LSB */

    /* DiSEqC registers */
    nim_stv0299_write(priv_mem->i2c_type_id, dev->base_addr,REG_DISEQC,       0X60); /* DiSEqC off */
    nim_stv0299_write(priv_mem->i2c_type_id, dev->base_addr,REG_DISEQC_FIFO,  0X00); /* FIFO */

    /* Input/Output configuration register */
    nim_stv0299_write(priv_mem->i2c_type_id, dev->base_addr,REG_IOCFG,        0XF1); /* OP1 ctl = Normal, OP1 val = 1 (LNB Power ON) */
    								  /* OP0 ctl = Normal, OP0 val = 1 (18 V)         */
    								  /* Nyquist filter = 00, QPSK reverse = 0        */
	/* AGC1 control register */
    nim_stv0299_write(priv_mem->i2c_type_id, dev->base_addr,REG_AGC1C,        0X81); /* DC offset compensation = ON, beta_agc1 = 2 */

	/* Timing loop register */
    nim_stv0299_write(priv_mem->i2c_type_id, dev->base_addr,REG_RTC,          0X23); /* alpha_tmg = 2, beta_tmg = 3 */
    nim_stv0299_write(priv_mem->i2c_type_id, dev->base_addr,REG_AGC1R,        0X19);
	nim_stv0299_write(priv_mem->i2c_type_id, dev->base_addr,REG_AGC2O,        0X3F); /* AGC2  0x3d */
    nim_stv0299_write(priv_mem->i2c_type_id, dev->base_addr,REG_TLSR,         0X84);
    nim_stv0299_write(priv_mem->i2c_type_id, dev->base_addr,REG_CFD,          0XF7); /* Lock detect: -64  Carrier freq detect:on */

    nim_stv0299_write(priv_mem->i2c_type_id, dev->base_addr,REG_ACLC,         0X98); /* alpha_car b:4 a:0  noise est:256ks  derot:on */
    nim_stv0299_write(priv_mem->i2c_type_id, dev->base_addr,REG_BCLC,         0X00); /* depend on symbol rate */
    nim_stv0299_write(priv_mem->i2c_type_id, dev->base_addr,REG_CLDT,         0X14); /* lock detector threshold */

    nim_stv0299_write(priv_mem->i2c_type_id, dev->base_addr,REG_AGC1I,        0X1D); /* AGC1 integrator value */
    nim_stv0299_write(priv_mem->i2c_type_id, dev->base_addr,REG_TLIR,         0X00);
    nim_stv0299_write(priv_mem->i2c_type_id, dev->base_addr,REG_AGC2I1,       0X14);
    nim_stv0299_write(priv_mem->i2c_type_id, dev->base_addr,REG_AGC2I2,       0XF2);

    nim_stv0299_write(priv_mem->i2c_type_id, dev->base_addr,REG_RTF,          0X11);
    nim_stv0299_write(priv_mem->i2c_type_id, dev->base_addr,REG_VSTATUS,      0X9C);
    nim_stv0299_write(priv_mem->i2c_type_id, dev->base_addr,REG_CLDI,         0X00);
    nim_stv0299_write(priv_mem->i2c_type_id, dev->base_addr,REG_ERRCNT_HIGH,  0X00);

    nim_stv0299_write(priv_mem->i2c_type_id, dev->base_addr,REG_ERRCNT_LOW,   0X0B);

    nim_stv0299_write(priv_mem->i2c_type_id, dev->base_addr,REG_SFRM,         0X50);
    nim_stv0299_write(priv_mem->i2c_type_id, dev->base_addr,REG_SFRL,         0X00);
    nim_stv0299_write(priv_mem->i2c_type_id, dev->base_addr,REG_CFRM,         0X00);
    nim_stv0299_write(priv_mem->i2c_type_id, dev->base_addr,REG_CFRL,         0X00);
    nim_stv0299_write(priv_mem->i2c_type_id, dev->base_addr,REG_NIRH,         0XFF);
    nim_stv0299_write(priv_mem->i2c_type_id, dev->base_addr,REG_NIRL,         0XFF);
    nim_stv0299_write(priv_mem->i2c_type_id, dev->base_addr,REG_VERROR,       0XFF);

    nim_stv0299_write(priv_mem->i2c_type_id, dev->base_addr,REG_FECM,         0X00); /* out imp: normal  out type: parallel FEC mode:0 */
    nim_stv0299_write(priv_mem->i2c_type_id, dev->base_addr,REG_VTH0,         0X1E); /* 1/2 threshold */
    nim_stv0299_write(priv_mem->i2c_type_id, dev->base_addr,REG_VTH1,         0X14); /* 2/3 threshold */
    nim_stv0299_write(priv_mem->i2c_type_id, dev->base_addr,REG_VTH2,         0X0F); /* 3/4 threshold */
    nim_stv0299_write(priv_mem->i2c_type_id, dev->base_addr,REG_VTH3,         0X09); /* 5/6 threshold */
    nim_stv0299_write(priv_mem->i2c_type_id, dev->base_addr,REG_VTH4,         0X05); /* 7/8 threshold */
    nim_stv0299_write(priv_mem->i2c_type_id, dev->base_addr,REG_PR,           0X1F); /* test all FECs */

    nim_stv0299_write(priv_mem->i2c_type_id, dev->base_addr,REG_VSEARCH,      0X19); /* viterbi and synchro search */
    nim_stv0299_write(priv_mem->i2c_type_id, dev->base_addr,REG_RS,           0XFC); /* rs control */
    nim_stv0299_write(priv_mem->i2c_type_id, dev->base_addr,REG_ERRCNT,       0X13); /* error control */
}

/*****************************************************************************
* INT32 nim_stv0299_open(struct nim_device *dev)
* Description: STV0299 open
*
* Arguments:
*  Parameter1: struct nim_device *dev
*
* Return Value: INT32
*****************************************************************************/
static INT32 nim_stv0299_open(struct nim_device *dev)
{
	nim_stv0299_hw_init(dev);
	return SUCCESS;
}

/*****************************************************************************
* INT32 nim_stv0299_close(struct nim_device *dev)
* Description: STV0299 close
*
* Arguments:
*  Parameter1: struct nim_device *dev
*
* Return Value: INT32
*****************************************************************************/
static INT32 nim_stv0299_close(struct nim_device *dev)
{

	return SUCCESS;
}

static void nim_stv0299_i2c_pass(struct nim_device *dev, int on_off)
{
	UINT8 data;
	struct nim_stv0299_private *priv_mem = (struct nim_stv0299_private *)dev->priv;
	if (on_off)
		data = 0x85;
	else
		data = 0x05;

	nim_stv0299_write(priv_mem->i2c_type_id, dev->base_addr, REG_I2CRPT, data);
}

/*****************************************************************************
* INT32 nim_stv0299_set_polar(struct nim_device *dev, UINT8 polar)
* Description: STV0299 set polarization
*
* Arguments:
*  Parameter1: struct nim_device *dev
*  Parameter2: UINT8 pol
*
* Return Value: void
*****************************************************************************/
static INT32 nim_stv0299_set_polar(struct nim_device *dev, UINT8 polar)
{
	struct nim_stv0299_private *priv_mem = (struct nim_stv0299_private *)dev->priv;
	HAL_GPIO_BIT_DIR_SET(priv_mem->polar_gpio_num, HAL_GPIO_O_DIR);
	switch (polar)
	{
	case NIM_PORLAR_HORIZONTAL:
		HAL_GPIO_BIT_SET(priv_mem->polar_gpio_num, 0);
		break;
	case NIM_PORLAR_VERTICAL:
		HAL_GPIO_BIT_SET(priv_mem->polar_gpio_num, 1);
		break;
	default:
        break;
	}

	return SUCCESS;
}

/*****************************************************************************
* INT32 nim_stv0299_set_12v(struct nim_device *dev, UINT8 flag)
* Description: STV0299 set LNB votage 12V enable or not
*
* Arguments:
*  Parameter1: struct nim_device *dev
*  Parameter2: UINT8 flag
*
* Return Value: SUCCESS
*****************************************************************************/
static INT32 nim_stv0299_set_12v(struct nim_device *dev, UINT8 flag)
{
	return SUCCESS;
}

/*****************************************************************************
* INT32 nim_stv0299_channel_change(struct nim_device *dev, UINT32 freq, UINT32 sym, UINT8 fec);
* Description: STV0299 channel change operation
*
* Arguments:
*  Parameter1: struct nim_device *dev
*  Parameter2: UINT32 freq				: Frequence
*  Parameter3: UINT32 sym				: Symbol rate
*  Parameter4: UINT8 fec				: Code rate
*
* Return Value: INT32
*****************************************************************************/
static INT32 nim_stv0299_channel_change(struct nim_device *dev, UINT32 freq, UINT32 sym, UINT8 fec)
{
	UINT8 lock = 0,data = 0;
	UINT8 dly_count = 0;
	INT8 direction = 1;
	INT16 derotator = 0;
	struct nim_stv0299_private * priv = (struct nim_stv0299_private *)dev->priv;
	MUTEX_LOCK();
	if((priv->cur_freq == freq)&&(priv->cur_sym == sym)&&(priv->cur_fec == fec))
	{
		nim_stv0299_get_FEC_lock(dev,&lock);
		if(lock == 1)
		{
			MUTEX_UNLOCK();
			return SUCCESS;
		}
	}
	/* Setup tuner */
	nim_stv0299_i2c_pass(dev, 1);/* Open Bus */
	priv->nim_Tuner_Control( priv->tuner_id, freq, sym);
	nim_stv0299_i2c_pass(dev, 0);/* Close Bus */

	/* Record the current parameters */
	priv->cur_freq = freq;
	priv->cur_sym = sym;
	priv->cur_fec = fec;

	nim_stv0299_write(priv->i2c_type_id, dev->base_addr, REG_CFRM, 0x00);
	nim_stv0299_write(priv->i2c_type_id, dev->base_addr, REG_CFRL, 0x00);
    	nim_stv0299_write(priv->i2c_type_id, dev->base_addr,REG_IOCFG, 0X51);
	/* setup puncture rate and synchro register */
	nim_stv0299_write(priv->i2c_type_id, dev->base_addr,REG_PR,(UINT8)0x1f);
	/* setup symbol rate */
	nim_stv0299_set_symbol_rate(dev,sym);

	osal_task_sleep(10);
	nim_stv0299_get_lock(dev,(UINT8 *)&lock);
	while(lock == 0)
	{
		dly_count++;
		/* Adjust freqency */
		derotator = dly_count*100*direction;
		direction=(direction>0) ? -1:1;
		nim_stv0299_write(priv->i2c_type_id, dev->base_addr, REG_CFRM, (derotator&0xFF00)>>8);
		nim_stv0299_write(priv->i2c_type_id, dev->base_addr, REG_CFRL, derotator&0x00FF);
		osal_task_sleep(10);
		nim_stv0299_get_lock(dev,(UINT8 *)&lock);
		if(lock == 0)
			nim_stv0299__check_inversion(dev);
		osal_task_sleep(10);
		nim_stv0299_get_lock(dev,(UINT8 *)&lock);
		if(lock == 1)
		{
			PRINTF("dly_count = %d\n",dly_count);
			break;
		}
		else
			nim_stv0299__check_inversion(dev);

		if(dly_count == 6)
			break;
	}
	MUTEX_UNLOCK();
	return SUCCESS;
}


static INT32 nim_stv0299_wait_QPSK_lock(struct nim_device *dev, UINT8 *qpsk_lock)
{
	return SUCCESS;
}

static INT32 nim_stv0299_channel_search(struct nim_device *dev, UINT32 freq)
{

	return SUCCESS;
}

/*****************************************************************************
* INT32 nim_stv0299_DiSEqC_operate(struct nim_device *dev, UINT32 mode, UINT8* cmd, UINT8 cnt)
*
*  defines DiSEqC operations
*
* Arguments:
*  Parameter1: struct nim_device *dev
*  Parameter2: UINT32 mode
*  Parameter3: UINT8* cmd
*  Parameter4: UINT8 cnt
*
* Return Value: void
*****************************************************************************/
INT32 nim_stv0299_DiSEqC_operate(struct nim_device *dev, UINT32 mode, UINT8* cmd, UINT8 cnt)
{
	return SUCCESS;
}

/*****************************************************************************
* INT32 nim_stv0299_get_freq(struct nim_device *dev, UINT32 *freq)
* Read stv0299 frequence
*
* Arguments:
*  Parameter1: struct nim_device *dev
*  Parameter2: UINT16 *sym_rate			: Symbol rate in kHz
*
* Return Value: void
*****************************************************************************/
static INT32 nim_stv0299_get_freq(struct nim_device *dev, UINT32 *freq)
{
	return SUCCESS;
}

/*****************************************************************************
* INT32 nim_stv0299_get_symbol_rate(struct nim_device *dev, UINT32 *sym_rate)
* Read stv0299 symbol rate
*
* Arguments:
*  Parameter1: struct nim_device *dev
*  Parameter2: UINT16 *sym_rate			: Symbol rate in kHz
*
* Return Value: void
*****************************************************************************/
static INT32 nim_stv0299_get_symbol_rate(struct nim_device *dev, UINT32 *sym_rate)
{
	struct nim_stv0299_private *priv_mem = (struct nim_stv0299_private *)dev->priv;
	UINT32 Mclk = M_CLK / 4096L;
	UINT32 srate;
	UINT32 offset;
	UINT8 sfr[3];
	UINT8 rtf;

	nim_stv0299_read(priv_mem->i2c_type_id, dev->base_addr, REG_SFRH,(UINT8*)sfr);
	nim_stv0299_read(priv_mem->i2c_type_id, dev->base_addr, REG_SFRM,(UINT8*)sfr+1);
	nim_stv0299_read(priv_mem->i2c_type_id, dev->base_addr, REG_SFRL,(UINT8*)sfr+2);

	nim_stv0299_read (priv_mem->i2c_type_id, dev->base_addr, REG_RTF, &rtf);
	srate = (sfr[0] << 8) | sfr[1];
	srate *= Mclk;
	srate /= 16;
	srate += (sfr[2] >> 4) * Mclk / 256;

	offset = (INT32) rtf * (srate / 4096L);
	offset /= 128;

	srate += offset;

	srate += 1000;
	srate /= 2000;
	srate *= 2000;
	*sym_rate = srate/1000;

	return SUCCESS;
}


/*****************************************************************************
* INT32 nim_stv0299_set_symbol_rate(struct nim_device *dev, UINT32 *sym_rate)
* Set stv0299 symbol rate
*
* Arguments:
*  Parameter1: struct nim_device *dev
*  Parameter2: UINT16 *sym_rate			: Symbol rate in kHz
*
* Return Value: void
*****************************************************************************/
static INT32 nim_stv0299_set_symbol_rate(struct nim_device *dev, UINT32 sym_rate)
{
	UINT32 ratio;
	UINT32 tmp;
	UINT8 aclk = 0xb4, bclk = 0x51;
	UINT32 srate = sym_rate*1000;
	struct nim_stv0299_private *priv_mem = (struct nim_stv0299_private *)dev->priv;
	if (srate > M_CLK)
		srate = M_CLK;
	if (srate < 500000)
		srate = 500000;
#if 0
	if (srate < 30000000) { aclk = 0xb6; bclk = 0x53; }
	if (srate < 14000000) { aclk = 0xb7; bclk = 0x53; }
	if (srate < 7000000) { aclk = 0xb7; bclk = 0x4f; }
	if (srate < 3000000) { aclk = 0xb7; bclk = 0x4b; }
	if (srate < 1500000) { aclk = 0xb7; bclk = 0x47; }
#else
	if(srate < 12500000) { aclk = 0x9b; bclk = 0x95;}
	else {aclk = 0x98; bclk = 0x95;}
#endif

#define FIN (M_CLK >> 4)

	tmp = srate << 4;
	ratio = tmp / FIN;

	tmp = (tmp % FIN) << 8;
	ratio = (ratio << 8) + tmp / FIN;

	tmp = (tmp % FIN) << 8;
	ratio = (ratio << 8) + tmp / FIN;
	nim_stv0299_write(priv_mem->i2c_type_id, dev->base_addr, REG_ACLC, aclk);
	nim_stv0299_write(priv_mem->i2c_type_id, dev->base_addr, REG_BCLC, bclk);
	nim_stv0299_write(priv_mem->i2c_type_id, dev->base_addr, REG_SFRH, (ratio >> 16) & 0xff);
	nim_stv0299_write(priv_mem->i2c_type_id, dev->base_addr, REG_SFRM, (ratio >>  8) & 0xff);
	nim_stv0299_write(priv_mem->i2c_type_id, dev->base_addr, REG_SFRL, (ratio      ) & 0xf0);

	return SUCCESS;
}

/*****************************************************************************
* INT32 nim_stv0299_get_code_rate(struct nim_device *dev, UINT8* code_rate)
* Description: Read stv0299 code rate
*   FEC status (b6-b4)  code rate  return value
*	0					1/2			1
*	1					2/3			2
*	2					3/4			3
*	3					5/6			5
*	4					6/7			6
*	5					7/8			7
*	6					invalid		8
*	7					invalid		9
*
* Arguments:
*  Parameter1: struct nim_device *dev
*  Parameter2: UINT8* code_rate
*
* Return Value: INT32
*****************************************************************************/
static INT32 nim_stv0299_get_code_rate(struct nim_device *dev, UINT8 *code_rate)
{
	return SUCCESS;
}

/*****************************************************************************
* INT32 nim_stv0299_get_AGC(struct nim_device *dev, UINT8 *agc)
*
*  This function will access the NIM to determine the AGC feedback value
*
* Arguments:
*  Parameter1: struct nim_device *dev
*  Parameter2: UINT16* agc
*
* Return Value: INT32
*****************************************************************************/
static INT32 nim_stv0299_get_AGC(struct nim_device *dev, UINT8 *agc)
{
	struct nim_stv0299_private *priv_mem = (struct nim_stv0299_private *)dev->priv;
	INT32 regval,Imin,Imax,i;
	UINT8 agc1,agc2;
	INT32 signal;

	nim_stv0299_read(priv_mem->i2c_type_id, dev->base_addr,REG_AGC2I1,(UINT8 *)&agc1);
	nim_stv0299_read(priv_mem->i2c_type_id, dev->base_addr,REG_AGC2I2,(UINT8 *)&agc2);

	regval = 0xFFFF - (((UINT16)agc1<< 8)|agc2);
	Imin = 0;
	Imax = sizeof(agc1_lookup)/sizeof(LOOKPOINT)-1;
	if(INRANGE(agc1_lookup[Imin].regval,regval,agc1_lookup[Imax].regval))
	{
		while((Imax-Imin)>1)
		{
			i=(Imax+Imin)/2;
			if(INRANGE(agc1_lookup[Imin].regval,regval,agc1_lookup[i].regval))
				Imax = i;
			else
				Imin = i;
		}

		*agc =	((regval - agc1_lookup[Imin].regval)
				* (agc1_lookup[Imax].realval - agc1_lookup[Imin].realval)
				/ (agc1_lookup[Imax].regval - agc1_lookup[Imin].regval))
				+ agc1_lookup[Imin].realval;
	}
	else
		*agc =  0;

	return SUCCESS;
}

/*****************************************************************************
* INT32 nim_stv0299_get_SNR(struct nim_device *dev, UINT8 *snr)
* Arguments:
*  Parameter1: struct nim_device *dev
*  Parameter2: UINT16* RsUbc
*
* Return Value: INT32
*****************************************************************************/
static INT32 nim_stv0299_get_SNR(struct nim_device *dev, UINT8 *snr)
{
	struct nim_stv0299_private *priv_mem = (struct nim_stv0299_private *)dev->priv;
	INT32 regval,Imin,Imax,i;
	UINT8 nirh,nirl;
	UINT8 lock = 0;
	*snr = 0;

	nim_stv0299_get_lock(dev,&lock);
	if(lock)
	{
		nim_stv0299_read(priv_mem->i2c_type_id, dev->base_addr,REG_NIRH,(UINT8 *)&nirh);
		nim_stv0299_read(priv_mem->i2c_type_id, dev->base_addr,REG_NIRL,(UINT8 *)&nirl);

		regval = ((UINT16)nirh << 8)|nirl;
		Imin = 0;
		Imax = sizeof(snr_lookup)/sizeof(LOOKPOINT)-1;

		if(INRANGE(snr_lookup[Imin].regval,regval,snr_lookup[Imax].regval))
		{
			while((Imax-Imin)>1)
			{
				i=(Imax+Imin)/2;
				if(INRANGE(snr_lookup[Imin].regval,regval,snr_lookup[i].regval))
					Imax = i;
				else
					Imin = i;
			}

			*snr =	((regval - snr_lookup[Imin].regval)
					* (snr_lookup[Imax].realval - snr_lookup[Imin].realval)
					/ (snr_lookup[Imax].regval - snr_lookup[Imin].regval))
					+ snr_lookup[Imin].realval;
		}
		else
			*snr = 100;
	}
	return SUCCESS;
}

/*****************************************************************************
* INT32 nim_stv0299_tuner_lock(struct nim_device *dev, UINT8 *tun_lock)
*
*  Read tuner lock status
*
* Arguments:
*  Parameter1: struct nim_device *dev
*  Parameter2: UINT8 *tun_lock
*
* Return Value: INT32
*****************************************************************************/
static INT32 nim_stv0299_tuner_lock(struct nim_device *dev, UINT8 *tun_lock)
{
	struct nim_stv0299_private * priv = (struct nim_stv0299_private *)dev->priv;
	*tun_lock = 0;
	/* Setup tuner */
	nim_stv0299_i2c_pass(dev, 1);
	priv->nim_Tuner_Status(priv->tuner_id, tun_lock);
	nim_stv0299_i2c_pass(dev, 0);

	return SUCCESS;
}

/*****************************************************************************
* INT32 nim_stv0299_get_QPSK_lock(struct nim_device *dev, UINT8 *qpsk_lock)
*
*  Read QPSK lock status
*
* Arguments:
*  Parameter1: struct nim_device *dev
*  Parameter2: UINT8 *qpsk_lock
*
* Return Value: INT32
*****************************************************************************/
static INT32 nim_stv0299_get_QPSK_lock(struct nim_device *dev, UINT8 *qpsk_lock)
{
	struct nim_stv0299_private *priv_mem = (struct nim_stv0299_private *)dev->priv;
	nim_stv0299_read(priv_mem->i2c_type_id, dev->base_addr,REG_VSTATUS,qpsk_lock);
//	PRINTF_TEST("qpsk = %x\n",*qpsk_lock);
	*qpsk_lock = (*qpsk_lock)>>7;
	return SUCCESS;
}

/*****************************************************************************
* INT32 nim_stv0299_get_FEC_lock(struct nim_device *dev, UINT8 *fec_lock)
*
*  Read FEC lock status
*
* Arguments:
*  Parameter1: struct nim_device *dev
*  Parameter2: BOOL *fec_lock
*
* Return Value: INT32
*****************************************************************************/
static INT32 nim_stv0299_get_FEC_lock(struct nim_device *dev, UINT8 *fec_lock)
{
	struct nim_stv0299_private *priv_mem = (struct nim_stv0299_private *)dev->priv;
	nim_stv0299_read(priv_mem->i2c_type_id, dev->base_addr,REG_VSTATUS,fec_lock);
//	PRINTF_TEST("fec = %x\n",*fec_lock);
	*fec_lock = ((*fec_lock)&0x10)>>4;
	return SUCCESS;
}

/*****************************************************************************
* INT32 nim_stv0299_get_lock(struct nim_device *dev, UINT8 *lock)
*
*  Read FEC lock status
*
*Arguments:
*  Parameter1: struct nim_device *dev
*  Parameter2: BOOL *fec_lock
*
*Return Value: INT32
*****************************************************************************/
static INT32 nim_stv0299_get_lock(struct nim_device *dev, UINT8 *lock)
{
	INT32 derot_freq;
	UINT8 temp1,temp2;
	struct nim_stv0299_private *priv_mem = (struct nim_stv0299_private *)dev->priv;
	UINT8 fec_lock, qpsk_lock,VSTATUS = 0;
	*lock = 0;
	nim_stv0299_read(priv_mem->i2c_type_id, dev->base_addr,REG_VSTATUS,&VSTATUS);
	nim_stv0299_get_QPSK_lock(dev, &qpsk_lock);
	nim_stv0299_get_FEC_lock(dev, &fec_lock);
	*lock = (fec_lock && qpsk_lock) ? 1 : 0;
//	PRINTF("@@qpsk_lock = %.2x\n@@",VSTATUS);

	return SUCCESS;
}

/*****************************************************************************
* INT32 nim_stv0299_get_QPSKBER(struct nim_device *dev, UINT32* qpsk_ber)
* Uses the viterbi error correction mechanism to
* determine the error rate out of the QPSK
* retrun value is 1E6 * the bit error rate
* error rate (*1e6)  = VIT_ERRCNT /(VIT_ERRPER *4)   *1e6
* error rate (*1e6) = VIT_ERRCNT *250000/VIT_ERRPER
*
* Arguments:
*  Parameter1: struct nim_device *dev
*  Parameter2: UINT32* qpsk_ber
*
* Return Value: INT32
*****************************************************************************/
static INT32 nim_stv0299_get_QPSKBER(struct nim_device *dev, UINT32* qpsk_ber)
{
	qpsk_ber = 0;
	return SUCCESS;
}


/*****************************************************************************
* INT32 nim_stv0299_get_PER(struct nim_device *dev, UINT32 *RsUbc)
* Reed Solomon Uncorrected block count
*
* Arguments:
*  Parameter1: struct nim_device *dev
*  Parameter2: UINT16* RsUbc
*
* Return Value: INT32
*****************************************************************************/
static INT32 nim_stv0299_get_PER(struct nim_device *dev, UINT32 *RsUbc)
{
	*RsUbc = 0;
	return SUCCESS;
}

/*****************************************************************************
* INT32 nim_stv0299_ioctl(struct nim_device *dev, INT32 cmd, UINT32 param)
*
*  device input/output operation
*
* Arguments:
*  Parameter1: struct nim_device *dev
*  Parameter2: INT32 cmd
*  Parameter3: UINT32 param
*
* Return Value: INT32
*****************************************************************************/
static INT32 nim_stv0299_ioctl(struct nim_device *dev, INT32 cmd, UINT32 param)
{
	struct nim_stv0299_private *priv_mem = (struct nim_stv0299_private *)dev->priv;
	INT32 result = SUCCESS;
	switch( cmd )
	{
	case NIM_DRIVER_READ_TUNER_STATUS:
	    return nim_stv0299_tuner_lock(dev, (UINT8 *)param);
	    break;
	case NIM_DRIVER_READ_QPSK_STATUS:
	    return nim_stv0299_get_QPSK_lock(dev, (UINT8 *)param);
	    break;
	case NIM_DRIVER_READ_FEC_STATUS:
	    return nim_stv0299_get_FEC_lock(dev, (UINT8 *)param);
	    break;
	case NIM_DRIVER_READ_QPSK_BER:
	    return nim_stv0299_get_QPSKBER(dev, (UINT32 *)param);
	    break;
	case NIM_DRIVER_READ_VIT_BER:
		result = !SUCCESS;
	    break;
	case NIM_DRIVER_READ_RSUB:
	    return nim_stv0299_get_PER(dev, (UINT32 *)param);
	    break;
	case NIM_DRIVER_UPDATE_PARAM:
		nim_stv0299_channel_change(dev, priv_mem->cur_freq, priv_mem->cur_sym, priv_mem->cur_fec);
		break;
	default:
	    break;
	}

	return result;
}

static INT32 nim_stv0299__check_inversion (struct nim_device *dev)
{
	UINT8 stat = 0;
	UINT8 val = 0;
	struct nim_stv0299_private *priv_mem = (struct nim_stv0299_private *)dev->priv;
	nim_stv0299_read(priv_mem->i2c_type_id, dev->base_addr, REG_VSTATUS,(UINT8 *)&stat);
	if((stat&0x98) != 0x98)
	{
		osal_task_sleep(30);
		nim_stv0299_read(priv_mem->i2c_type_id, dev->base_addr, REG_VSTATUS,(UINT8 *)&stat);
		if((stat&0x98) != 0x98)
		{
			nim_stv0299_read(priv_mem->i2c_type_id, dev->base_addr, REG_IOCFG,(UINT8 *)&val);
			PRINTF("nim_stv0299__check_inversion:toggle inversion\n");
			return nim_stv0299_write(priv_mem->i2c_type_id, dev->base_addr, REG_IOCFG, val ^ 0x01);
		}
	}
	return SUCCESS;
}

